﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using MyFloridaHouse.libSvCommonUtilities;
using MyFloridaHouse.libSvOpenDistricts.DataService;
using MyFloridaHouse.libSvOpenDistricts.Districts;
using MyFloridaHouse.libSvOpenDistricts.enumerations;
using MyFloridaHouse.libSvOpenDistricts.MapShapes;
using MyFloridaHouse.libSvOpenDistricts.MapTools;
using MyFloridaHouse.libSvUtilities;
using MyFloridaHouse.libSvUtilities.Logging;
using MyFloridaHouse.ServiceConn.DistrBldrSvcRef;
using MyFloridaHouse.libSvOpenDistricts.Census;
using MyFloridaHouse.libSvOpenDistricts.SummaryDataGrid;

namespace MyFloridaHouse.libSvOpenDistricts.ViewModel
{
    public enum DistrictBuildTypes
    {
        Lasso,
        Cut,
        Paste,
        Undo,
        Shaded
    }

    public partial class ViewModelBase
    {
        // district build is a two step process 
        // 1. get IDs to add/remove from shapes (in progress)
        // 2.ask the server to build the shapes (multiple shapes) 
        // districtBuildInProgress indicates true for either condition
        private bool _districtBuildInProgress = false;
        public bool districtBuildInProgress
        {
            get { return _districtBuildInProgress; }
            set
            {
                if (_districtBuildInProgress != value)
                {
                    _districtBuildInProgress = value;
                    NotifyPropertyChanged("districtBuildInProgress");
                }
            }
        }

        private int _districtBuildRequestCount = 0;
        public int districtBuildRequestCount
        {
            get { return _districtBuildRequestCount; }
            set
            {
                if (_districtBuildRequestCount != value)
                {
                    _districtBuildRequestCount = value;
                    NotifyPropertyChanged("districtBuildRequestCount");

                }
                // we may set it to zero when it's already zero, if so we still want to trigger turning off the districtBuildInProgress indicator
                if (0 == _districtBuildRequestCount)
                {
                    districtBuildInProgress = false;
                }
            }
        }

        public enumLassoMode getLassoMode()
        {
            if (mapMode == enumMapMode.Click)
                return enumLassoMode.Click;
            else if (mapMode == enumMapMode.Lasso)
                return enumLassoMode.Lasso;
            else
                return enumLassoMode.Off;
        }

        public void OnLassoCompleted()
        {
            if (districtBuildInProgress)
            {
                Log.NotifyUser("Sorry, a build is in progress.  Please wait");
            }
            else
            {
                BuildDistrictUsing(DistrictBuildTypes.Lasso);
            }
        }

        public void BuildDistrictUsing(DistrictBuildTypes buildType)
        {
            // All builds come through here so we can make sure build indicator doesn't lock user interface
            // NOTE:  Copy just takes a copy of the current district and writes it to the clipboard in isolated storage.  No Build function involved
            try
            {
                bool success = false;
                // if we get out of sync, this helps us get back in without requiring a restart
                if (districtBuildRequestCount < 0)
                {
                    districtBuildRequestCount = 0;
                }

                if (!districtBuildInProgress)
                {
                    bool submitShapes = false;
                    switch (buildType)
                    {
                        case DistrictBuildTypes.Lasso:
                            {
                                success = GetLassoUogIDs();
                                break;
                            }
                        case DistrictBuildTypes.Cut:
                            {
                                success = districtPlan.DistrictCut();
                                if (success)
                                {
                                    submitShapes = true;
                                }
                                break;
                            }
                        case DistrictBuildTypes.Paste:
                            {
                                success = districtPlan.DistrictPaste();
                                if (success)
                                {
                                submitShapes = true;
                                }
                                break;
                            }
                        case DistrictBuildTypes.Undo:
                            {
                                success = districtPlan.UndoChanges();
                                                                if (success)
                                {
                                submitShapes = true;
                                                                }
                                break;
                            }
                        case DistrictBuildTypes.Shaded:
                            {
                                success = MoveShadedRegionsToDistrict();
                                                                if (success)
                                {
                                submitShapes = true;
                                                                }
                                break;
                            }
                        default:
                            {
                                throw new Exception("Invalid district build type");
                            }
                    }
                    if (success)
                    {
                        districtBuildInProgress = true;

                        if (submitShapes)
                        {
                            if (!BuildDistrictShapesChanged())
                            {
                                // something wrong in building
                                districtBuildInProgress = false;
                            }
                        }
                    }
                }

                if( success & (DistrictBuildTypes.Lasso == buildType))
                {
                    mapLasso.Reset(getLassoMode());
                }
            }

            catch (Exception ex)
            {
                districtBuildRequestCount=0;
                Log.HandleInternalError("Error building district : " + ex.Message);
            }

        }

        public bool GetLassoUogIDs()
        {
            // turn the lasso shape from the wkt into a list of uogIDs
            serviceDAL.GetShapeUogIDs
                    (mapLasso.GetWKT()
                    , buildLayerList[buildLayerListSelectedIndex].layerID       // get the layer being used as building blocks
                    , districtPlan.selectedDistrict
                    , new System.EventHandler<GetShapeUogIDsCompletedEventArgs>(GetLassoUogIDsCompleted));
            return true;
        }

        public void GetLassoUogIDsCompleted(object sender, GetShapeUogIDsCompletedEventArgs args)
        {
            bool success = false;
            mapLasso.Reset(getLassoMode());
            try
            {
                CheckForServerTimeoutGettingLassoUogIDs(args);

                if (!ServiceDAL.IsError("GetShapeUogIDsCompleted", args.Error, args.Result, args.Result.errorMessage))
                {

                    // Everything OK!
                    List<UOG> uogIDList = new List<UOG>();
                    foreach (var uog in args.Result.uogidList)
                    {
                        uogIDList.Add(new UOG(uog));
                    }

                    if (uogIDList.Count > Constants.MaxDistrictAddShapeCount)
                    {
                        Log.NotifyUser("You selected " + uogIDList.Count.ToString() + " shapes to add, please limit selection to no more than " + Constants.MaxDistrictAddShapeCount.ToString() + " shapes.");
                    }
                    else
                    {
                        string rejectedUogIDList = censusGeometry.ScrubUogIDList(uogIDList);
                        if (rejectedUogIDList != null)
                        {
                            if (0 != rejectedUogIDList.Length)
                            {
                                string errorMessage = "The following geographies were not added to the district [" + rejectedUogIDList + "]"
                                    + Environment.NewLine
                                    + "All other selected geography was  added."
                                    + Environment.NewLine
                                    + "Please email us at myDistrictBuilder@myFloridaHouse.gov with any information you can about this error."
                                    + ")";
                                Log.HandleInternalError(errorMessage);
                            }
                        }
                        if (districtPlan.MoveShapesToNewDistrict(args.Result.districtID, uogIDList))
                        {
                            success = BuildDistrictShapesChanged();
                        }
                    }

                }


            }
            catch (Exception ex)
            {
                Log.HandleInternalError("Problem reassigning elements to district(s): " + ex.Message.ToString() + " for IDs(" + string.Join(",", args.Result.uogidList));
            }
            if (!success)
            {
                districtBuildRequestCount = 0;
            }
        }

        private static void CheckForServerTimeoutGettingLassoUogIDs(GetShapeUogIDsCompletedEventArgs args)
        {
            if (null != args)
            {
                if (null != args.Result)
                {
                    if (args.Result.errorMessage.Contains("Timeout"))
                    {
                        Log.NotifyUser("You got a timeout error, you may be trying to build too many shapes"
                            + Environment.NewLine
                            + "Try selecting a smaller area");
                    }
                }
            }
        }

        public bool MoveShadedRegionsToDistrict()
        {
            int buildDistrictID = districtPlan.districtSelectedIndex;
            List<UOG> uogIDList = new List<UOG>();
            foreach (var rgn in buildLayerList[buildLayerListSelectedIndex].layerRegionList)
            {
                //NOTE: looking for any region on the build layer that has fill turned on
                if (!((SolidColorBrush)rgn.regionShape.Fill).Color.Equals(Colors.Transparent))
                {
                    uogIDList.Add(new UOG(rgn.regionID));
                }
            }
            return districtPlan.MoveShapesToNewDistrict(buildDistrictID, uogIDList);
        }


        private bool BuildDistrictShapesChanged()
        {
            bool result = false;
            // now all shapes have been reassigned, so redisplay every changed district
            List<string> condensedDistrictList = CensusGeometryCondensed.ToCondensedList(districtPlan.districtCount);
            if (0 != districtPlan.changedDistrictsList.Count)
            {
                SummaryDataGridManager.ResetAllChangedIndicators();
                foreach (var districtID in districtPlan.changedDistrictsList)
                {
                    // display each district using the shapes left in them after reassignment
                    //todo: check this for out of range index
                    districtMapLayer.RemoveRegion(districtID);
                    districtPlan.ClearDistrictShape(districtID);
                    GetDistrictShape(districtID, condensedDistrictList[districtID]);
                    districtBuildRequestCount++;
                }

                SummaryDataGridManager.BuildChangedDistrictSummaries(districtPlan.changedDistrictsList, condensedDistrictList);

                result = true;
            }

            return result;
        }

        public void BuildDistrictShapesAll()
        {
            // for each known district,  ignore zero(unassinged) 
            // look in censusGeometry for it's condensed version
            // send the condensed version to the database

            List<string> condensedDistrictList = CensusGeometryCondensed.ToCondensedList(districtPlan.districtCount);
            for (int districtID = 1; districtID < condensedDistrictList.Count; districtID++)
            //    for (int districtID = 1; districtID < condensedDistrictList.Count; districtID++)
            {
                // no point in building the shapes if there isn't anything in it
                if (condensedDistrictList[districtID].Length > 0)
                {
                    GetDistrictShape(districtID, condensedDistrictList[districtID]);
                    districtBuildRequestCount++;
                }
            }


            SummaryDataGridManager.BuildAllDistrictSummaries();


        }


        private void GetDistrictShape(int districtID, string condensedDistrictList)
        {
            serviceDAL.GetDistrictShape(districtID, condensedDistrictList, new System.EventHandler<GetDistrictShapeCompletedEventArgs>(GetDistrictShapeCompleted));
        }

        public void GetDistrictShapeCompleted(object sender, GetDistrictShapeCompletedEventArgs args)
        {
            districtBuildRequestCount--;
            serviceDAL.getDistrictShapeRequestCount--;

            if (!ServiceDAL.IsError("GetDistrictShapeCompleted: District [" + args.Result.districtID.ToString() + "Shape can't be drawn - try adding/removing geography to re-generate district shape  : ", args.Error, args.Result, args.Result.errorMessage))
            {
                DistrictShapeSTO result = args.Result;

                try
                {

                    // don't display the shape for the default (unassigned) district (district 0)
                    if (result.districtID != 0)
                    {
                        byte[] decompressed = CompressionManager.DecompressToByteArray((byte[])result.mapMultiShapeDTOCompressed);
                        result.mapMultiShapeDTO = (MapMultiShapeDTO)SerializationManager.Deserialize(typeof(MapMultiShapeDTO), decompressed);
                        districtPlan.UpdateDistrictMetaData(result);

                        // save the full detail district shape for KMZ file output
                        districtPlan.UpdateDistrictShape(result.districtID, MapShapeTools.ConvertServiceDTOtoLibSvDTO(result.mapMultiShapeDTO));

                        districtPlan.ShowNewDistrictShape(result.districtID);
                    }


                }
                catch (Exception ex)
                {
                    string districtID = "unknown";
                    if (result != null)
                    {
                        districtID = result.districtID.ToString();
                    }
                    Log.HandleInternalError("Unable to draw new shape for district [" + districtID + "] : " + ex.Message.ToString());
                }
            }

            mapLasso.Hide();
            mapLasso.Reset(getLassoMode());

        }
    }
}
